#include "buildRenderer.h"
#include "buildCamera.h"

#include "samplers/adaptive.h"

#include "renderers/aggregatetest.h"
#include "renderers/createprobes.h"
#include "renderers/metropolis.h"
#include "renderers/samplerrenderer.h"
#include "renderers/surfacepoints.h"

#include "integrators/whitted.h"
#include "integrators/single.h"

#include <iostream>


Renderer *getRenderer( FnKat::Render::RenderSettings &renderSettings,
                       FnKat::FnScenegraphIterator rootIt,
                       BasicSettings* basicSettings )
{
    // Default renderer, sampler and integrator
    string rendererType   = "sampler";
    string samplerType    = "adaptive";
    string surfIntegrType = "whitted";
    string volIntegrType  = "single";

    FnKat::GroupAttribute globalSettingsAttr = rootIt.getAttribute("pbrtGlobalSettings");
    FnKat::GroupAttribute rendererParamsAttr;
    FnKat::GroupAttribute samplerParamsAttr;
    FnKat::GroupAttribute surfIntegrParamsAttr;
    FnKat::GroupAttribute volIntegrParamsAttr;

    if ( globalSettingsAttr.isValid() )
    {
        FnKat::GroupAttribute rendererAttr = globalSettingsAttr.getChildByName("renderer");
        if(rendererAttr.isValid())
        {
            FnKat::GroupAttribute paramsAttr;

            // Renderer Type
            FnKat::StringAttribute rendererTypeAttr = rendererAttr.getChildByName("type");
            if(rendererTypeAttr.isValid())
                rendererType = rendererTypeAttr.getValue();

            // Renderer Parameters
            paramsAttr = rendererAttr.getChildByName("rendererParams");
            if(paramsAttr.isValid())
            {
                rendererParamsAttr = paramsAttr.getChildByName(rendererType);
            }

            // Sampler
            FnKat::StringAttribute samplerTypeAttr = rendererAttr.getChildByName("sampler");
            if(samplerTypeAttr.isValid())
                samplerType = samplerTypeAttr.getValue();

            // Sampler Parameters
            paramsAttr = rendererAttr.getChildByName("samplerParams");
            if(paramsAttr.isValid())
            {
                samplerParamsAttr = paramsAttr.getChildByName(samplerType);
            }


            // Surface Integrator
            FnKat::StringAttribute surfIntegrTypeAttr = rendererAttr.getChildByName("surfaceIntegrator");
            if(surfIntegrTypeAttr.isValid())
                surfIntegrType = surfIntegrTypeAttr.getValue();

            // Surface Integrator Parameters
            paramsAttr = rendererAttr.getChildByName("surfaceIntegratorParams");
            if(paramsAttr.isValid())
            {
                surfIntegrParamsAttr = paramsAttr.getChildByName(surfIntegrType);
            }


            // Volume Integrator
            FnKat::StringAttribute volIntegrTypeAttr = rendererAttr.getChildByName("volumeIntegrator");
            if(volIntegrTypeAttr.isValid())
                volIntegrType = volIntegrTypeAttr.getValue();

            // Volume Integrator Parameters
            paramsAttr = rendererAttr.getChildByName("volumeIntegratorParams");
            if(paramsAttr.isValid())
            {
                volIntegrParamsAttr = paramsAttr.getChildByName(volIntegrType);
            }
        }
    }

    // Create the camera, sampler and integrators
    Camera *camera = getCamera( renderSettings, rootIt, basicSettings );
    if (camera == NULL)
    {
        return NULL;
    }

    Sampler *sampler = getSampler( renderSettings, samplerType,
                                   samplerParamsAttr, camera->film, camera);

    SurfaceIntegrator *surfIntegrator = getSurfaceIntegrator( renderSettings,
                                                              surfIntegrType,
                                                              surfIntegrParamsAttr );

    VolumeIntegrator *volIntegrator = getVolumeIntegrator( renderSettings,
                                                           volIntegrType,
                                                           volIntegrParamsAttr );

    // Create the renderer and return it
    if( rendererType == "sampler" )
    {
        bool visIds = false;
        if ( rendererParamsAttr.isValid() )
        {
            FnKat::IntAttribute visIdsAttr = rendererParamsAttr.getChildByName("visualizeIds");
            if(visIdsAttr.isValid())
                visIds = (bool)visIdsAttr.getValue();
        }

        return new SamplerRenderer(sampler, camera, surfIntegrator,
                                   volIntegrator, visIds);
    }
    else if( rendererType == "metropolis" )
    {
        ParamSet p;

        setFloatParam(p, rendererParamsAttr, "LargestStepProbability",
                      "largestepprobability");

        setIntParam(p, rendererParamsAttr, "SamplesPerPixel",
                    "samplesperpixel");

        setIntParam(p, rendererParamsAttr, "BootStrapSamples",
                    "bootstrapsamples");

        setIntParam(p, rendererParamsAttr, "DirectSamples",
                    "directsamples");

        setBoolParam(p, rendererParamsAttr, "DoDirectSeparately",
                    "dodirectseparately");

        setIntParam(p, rendererParamsAttr, "MaxConsecutiveRejects",
                    "maxconsecutiverejects");

        setIntParam(p, rendererParamsAttr, "MaxDepth",
                    "maxdepth");

        setBoolParam(p, rendererParamsAttr, "DoBidirectional",
                    "bidirectional");

        return CreateMetropolisRenderer(p, camera);
    }

    return NULL;
}


Sampler *getSampler( FnKat::Render::RenderSettings &renderSettings,
                     const string &samplerType,
                     const FnKat::GroupAttribute &samplerParamsAttr,
                     const Film *film, const Camera *camera)
{
    ParamSet p;

    if(samplerType == "adaptive")
    {
        // TODO get values from samplerParamsAttr into p
        return CreateAdaptiveSampler(p, film, camera);
    }

    return NULL;
}


SurfaceIntegrator *getSurfaceIntegrator( FnKat::Render::RenderSettings &renderSettings,
                                         const string &type,
                                         const FnKat::GroupAttribute &surfIntegrParamsAttr)
{
    ParamSet p;

    if(type == "whitted")
    {
        return CreateWhittedSurfaceIntegrator(p);
    }

    return NULL;
}


VolumeIntegrator *getVolumeIntegrator( FnKat::Render::RenderSettings &renderSettings,
                                       const string &type,
                                       const FnKat::GroupAttribute &volIntegrParamsAttr)
{
    ParamSet p;

    if(type == "single")
    {
        return CreateSingleScatteringIntegrator(p);
    }

    return NULL;
}
